package com.calvin.study.multithread.threadutils;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//生产者、消费者实现的目的是 生产者和消费者线程配对执行，比如AB 或CD 或AD ...
public class ProducerConsumerDemo {

	public static void main(String[] args) {
		//falseNoticeBySync();
		falseNoticByLock();
		
		//sequenceExec();
	}

	//多线程，顺序调用
	public static void sequenceExec() {
		Data3 data = new Data3();// 资源类
		
		//A\B\C三个线程执行5次
		new Thread(() -> {
			for (int i = 0, len = 5; i < len; i++) {
				//a线程打印1-5
				data.methodA();
			}
		}, "A").start();
		
		new Thread(() -> {
			for (int i = 0, len = 5; i < len; i++) {
				//b线程打印1-10
				data.methodB();
			}
		}, "B").start();
		
		new Thread(() -> {
			for (int i = 0, len = 5; i < len; i++) {
				//c线程打印1-15
				data.methodC();
			}
		}, "C").start();

	}
	
	//A,B,C,D线程模拟生产和消费各10组（生产一个消费一个为一组）
	public static void falseNoticByLock() {
		Data2 data = new Data2();// 资源类
		
		//执行过程中，谁先抢占线程，则谁开始执行。
		//若当前线程满足条件，则await()等待，切换到下一个线程开始执行。
		//利用number来控制当前线程是否执行，相当于信号灯。
		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.increment();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "A").start();

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.decrement();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "B").start();

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.increment();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "C").start();

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.decrement();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "D").start();
	}

	public static void falseNoticeBySync() {
		Data data = new Data();// 资源类

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.increment();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "A").start();

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.decrement();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "B").start();

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.increment();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "C").start();

		new Thread(() -> {
			for (int i = 0, len = 10; i < len; i++) {
				try {
					data.decrement();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}, "D").start();
	}
}

//顺序通知，依次调用
class Data3 {
	//执行标识 判断该第几个线程执行
	private int number = 1;

	private Lock lock = new ReentrantLock();
	Condition con1 = lock.newCondition();
	Condition con2 = lock.newCondition();
	Condition con3 = lock.newCondition();

	public void methodA() {
		lock.lock();
		try {
			while (number != 1) {//如果不该A执行，则轮到B执行
				con1.await();
			}
			for(int i = 1;i<=5;i++) {
				System.out.println("当前线程:" + Thread.currentThread().getName() + "\t value=>" + i);
			}
			//设置执行的调用标识
			number = 2;
			//唤醒指定的线程
			con2.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void methodB() {
		lock.lock();
		try {
			while (number != 2) {
				con2.await();
			}
			for(int i = 1;i<=10;i++) {
				System.out.println("当前线程:" + Thread.currentThread().getName() + "\t value=>" + i);
			}
			number = 3;
			con3.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void methodC() {
		lock.lock();
		try {
			while (number != 3) {
				con3.await();
			}
			for(int i = 1;i<=15;i++) {
				System.out.println("当前线程:" + Thread.currentThread().getName() + "\t value=>" + i);
			}
			number = 1;
			con1.signal();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

//资源类
class Data2 {

	private int number = 0;

	Lock lock = new ReentrantLock();
	Condition c = lock.newCondition();

	public void increment() throws InterruptedException {
		lock.lock();
		try {
			while (number != 0) {
				c.await();//当前线程等待
			}
			number++;
			System.out.println("当前线程:" + Thread.currentThread().getName() + "=>" + number);
			c.signalAll();//换新全部线程
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			lock.unlock();
		}

	}

	public void decrement() throws InterruptedException {
		lock.lock();
		try {
			while (number == 0) {
				c.await();
			}
			number--;
			System.out.println("当前线程:" + Thread.currentThread().getName() + "=>" + number);
			c.signalAll();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			lock.unlock();
		}

	}
}

//资源类
class Data {

	private int number = 0;

	/**
	 * 虚假通知问题产生：increment() decrement()方法中的if只判断一次 虚假通知问题结局：将if 改为 while
	 */
	public synchronized void increment() throws InterruptedException {
		while (number != 0) {
			this.wait();
		}
		number++;
		System.out.println("当前线程:" + Thread.currentThread().getName() + "=>" + number);
		this.notifyAll();
	}

	public synchronized void decrement() throws InterruptedException {
		while (number == 0) {
			this.wait();
		}
		number--;
		System.out.println("当前线程:" + Thread.currentThread().getName() + "=>" + number);
		this.notifyAll();
	}
}
